package com.sanbi.shield.client.core.utils.single;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;

import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Shield表达式计算器
 *
 * @param <T> 结果类型
 * @author XiaoQingqing
 * @since 2024/08/22
 */
public class ShieldExpressionEvaluator<T> extends CachedExpressionEvaluator {
    /**
     * 参数名称解析
     */
    private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
    /**
     * 条件缓存
     */
    private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);
    /**
     * 目标方法缓存
     */
    private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<>(64);

    /**
     * 创建上下文
     *
     * @param targetObject 目标对象
     * @param targetClass  目标类
     * @param method       方法
     * @param args         访问参数
     * @return 上下文
     */
    public EvaluationContext createContext(Object targetObject, Class<?> targetClass, Method method, Object[] args) {
        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
        Method targetMethod = this.targetMethodCache.get(methodKey);
        if (targetMethod == null) {
            targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
            this.targetMethodCache.put(methodKey, targetMethod);
        }
        ExpressionRootObject root = new ExpressionRootObject(targetObject, args);
        return new MethodBasedEvaluationContext(root, targetMethod, args, this.paramNameDiscoverer);
    }

    /**
     * 计算表达式
     *
     * @param conditionExpression 条件表达式
     * @param elementKey          元素键
     * @param evalContext         上下文
     * @param resultClass         结果类
     * @return 计算结果
     */
    public T evaluateExpression(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class<T> resultClass) {
        return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext, resultClass);
    }

    /**
     * 表达式根对象
     */
    @Data
    @AllArgsConstructor
    public static class ExpressionRootObject implements Serializable {
        @Serial
        private static final long serialVersionUID = 1L;
        /**
         * 目标对象
         */
        private Object object;
        /**
         * 访问参数
         */
        private Object[] args;
    }
}
